const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'


class MPromise {
  FULFILLED_CALLBACK_LIST = []
  REJECTED_CALLBACK_LIST = []
  _status = PENDING
  constructor(fn) {
    this.status = PENDING
    this.value = null
    this.reason = null
    try {
      fn(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }

  get status() {
    return this._status
  }

  set status(newStatus) {
    this._status = newStatus
    switch (this._status) {
      case FULFILLED: {
        this.FULFILLED_CALLBACK_LIST.forEach(cb => cb(this.value))
        break
      }
      case REJECTED: {
        this.REJECTED_CALLBACK_LIST.forEach(cb => cb(this.reason))
        break
      }
    }
  }

  resolve(value) {
    if (this.status === PENDING) {
      this.value = value
      this.status = FULFILLED
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
    }
  }
  then(onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
    const realOnRejected = thsi.isFunction(onRejected) ? onRejected : reason => reason
    const promise2 = new MPromise((resolve, reject) => {
      const fulfilledMicrotask = ()=>{
        try {
          const x = realOnFulfilled()
          this.resolvePromise(promise2,x, resolve,reject)
        } catch (error) {
          reject(error)
        }
      }
      const rejectedMicrotask = ()=>{
        try {
          const x = realOnRejected()
          this.resolvePromise(promise2,x,resolve,reject)
        } catch (error) {
          reject(error)
        }
      }

      switch (this.status) {
        case FULFILLED: {
          fulfilledMicrotask()
          break
        }
        case REJECTED: {
          rejectedMicrotask()
          break
        }
        case PENDING: {
          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
          this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
          break
        }
      }
    })
    return promise2
  }
  resolvePromise(promise2,x,resolve,reject){
    if (promise2 === x) {
      return reject(new TypeError('promise and the return value are the same'))
    }
    if (x instanceof MPromise) {
      queueMicrotask(()=>{
        x.then(y=>{
          this.resolvePromise(promise2,y,resolve, reject)
        },reject)
      })
    } else if (typeof x === 'object' || this.isFunction(x)) {
      if (x === null) {
        return resolve(x)
      }
      let then = null
      try {
        then = x.then
      } catch (error) {
        return reject(error)
      }
      if (this.isFunction(then)) {
        let called = false
        try {
          then.call(
            x,
            (y)=>{
              if (called) return
              this.resolvePromise(promise2,y,resolve, reject)
            },
            (r)=>{
              if (called) return
              reject(r)
            }
          )
        } catch (error) {
          if (called) return
          reject(error)
        }
      } else {
        resolve(x)
      }

    } else {
      resolve(x)
    }
  }
  catch(onRejected){
    this.then(null, onRejected)
  }
  isFunction(fn) {
    return typeof fn === 'function'
  }
}


var p1 = new Promise((resolve,reject)=>{
  console.log(p1)
  resolve('222')
}).then(res=>{
  console.log(res)
  console.log(p1)
})